﻿using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using Common.Logging;

namespace TSharp.Core.Asm
{
    /// <summary>
    /// OSGI配置引擎
    /// <para>2011/3/4</para>
    /// 	<para>TANGJINGBO</para>
    /// 	<author>tangjingbo</author>
    /// </summary>
    public sealed class SettingsEngine
    {
        private static ILog log = LogManager.GetLogger("SettingsEngine");
        /// <summary>
        /// 获取当前引擎单例
        /// </summary>
        /// <value>The current.</value>
        public static SettingsEngine Current { get; private set; }

        /// <summary>
        /// 根物理路径
        /// </summary>
        public string RootPath { private set; get; }

        /// <summary>
        /// 程序集库路径
        /// </summary>
        public string LibPath { get; private set; }

        /// <summary>
        /// Gets or sets the runtime path.
        /// </summary>
        /// <value>The runtime path.</value>
        public string RuntimePath { get; private set; }

        /// <summary>
        /// Gets or sets a value indicating whether [disable ext attr load exception].
        /// </summary>
        /// <value>
        /// 	<c>true</c> if [disable ext attr load exception]; otherwise, <c>false</c>.
        /// </value>
        public bool DisableExtAttrLoadException { get; set; }

        /// <summary>
        /// 主程序集类Type
        /// </summary>
        public Type Clazz { get; private set; }

        private readonly Dictionary<Type, ExtensionPoint> _extensionPoints;
        private readonly Dictionary<string, VersionAssembly> _assemblys;

        /// <summary>
        /// Initializes a new instance of the <see cref="SettingsEngine"/> class.
        /// </summary>
        /// <param name="rootPath">The root path.</param>
        /// <param name="runtimePath">The runtime path.</param>
        /// <param name="libPath">The lib path.</param>
        /// <param name="clazz">The clazz.</param>
        public SettingsEngine(string rootPath, string runtimePath, string libPath, Type clazz)
            : this(rootPath, runtimePath, libPath, clazz, false)
        {
        }

        /// <summary>
        /// OSGI 引擎
        /// </summary>
        /// <param name="rootPath">The root path.</param>
        /// <param name="runtimePath">The runtime path.</param>
        /// <param name="libPath">程序集库路径</param>
        /// <param name="clazz">主程序集类Type</param>
        /// <param name="disableExtAttrLoadException">if set to <c>true</c> [disable ext attr load exception].</param>
        public SettingsEngine(string rootPath, string runtimePath, string libPath, Type clazz,
                              bool disableExtAttrLoadException)
        {
            RootPath = rootPath;
            DisableExtAttrLoadException = disableExtAttrLoadException;
            RuntimePath = runtimePath;
            LibPath = libPath;
            Clazz = clazz;
            _extensionPoints = new Dictionary<Type, ExtensionPoint>();
            _assemblys = new Dictionary<string, VersionAssembly>();
            InitAssembly(Clazz.Assembly);
            if (!String.IsNullOrEmpty(runtimePath))
            {
                var dllFiles = Directory.GetFiles(runtimePath, "*.dll");
                foreach (string dllFile in dllFiles)
                {
                    Assembly assembly = null;
                    try
                    {
                        assembly = Assembly.LoadFrom(dllFile);
                    }
                    catch (BadImageFormatException e)
                    {
                        log.Error("配置引擎无法加载DLL：" + dllFile, e);
                        continue;
                    }
                    if (assembly != null) InitAssembly(assembly);
                }
            }
            if (!string.IsNullOrEmpty(libPath) && Directory.Exists(libPath))
            {
                var dllFiles = Directory.GetFiles(libPath, "*.dll");
                foreach (string dllFile in dllFiles)
                {
                    Assembly assembly = null;
                    try
                    {
                        assembly = Assembly.LoadFrom(dllFile);
                    }
                    catch (BadImageFormatException e)
                    {
                        log.Error("配置引擎无法加载DLL：" + dllFile, e);
                        continue;
                    }
                    if (assembly != null) InitAssembly(assembly);
                }
            }
            var extensions = new HashSet<ExtensionAttributeItem>();
            foreach (string key in _assemblys.Keys)
            {
                RegisterAssembly(_assemblys[key].LatestVersionAssembly, extensions);
            }
            foreach (ExtensionAttributeItem extension in extensions)
            {
                if (_extensionPoints.ContainsKey(extension.ExtensionAttribute.GetType()))
                {
                    _extensionPoints[extension.ExtensionAttribute.GetType()].Register(extension.Assembly,
                                                                                      extension.ExtensionAttribute);
                }
            }
            Current = this;
            foreach (var extension in _extensionPoints.Values)
            {
                extension.OnInit();
            }
            foreach (var extension in _extensionPoints.Values)
            {
                extension.OnLoad();
            }
            //为什么所有类型都要创建一次？？ 
            //这是绝不允许的操作！！ by tjb 20110126
            //if (!disableExtAttrLoadException)
            //{
            //    foreach (var assembly in _assemblys)
            //    {
            //        Type[] allTypes = assembly.Value.CurrentVersionAssembly.GetTypes();
            //        for (int i = 0; i < allTypes.Length; i++)
            //        {
            //            if (!allTypes[i].ContainsGenericParameters && !allTypes[i].IsAbstract)
            //            {
            //                Activator.CreateInstance(allTypes[i]);
            //            }
            //        }
            //    }
            //}
        }

        /// <summary>
        /// 加载程序集文件
        /// </summary>
        /// <param name="dllFile">The DLL file.</param>
        public void LoadSinleFile(string dllFile)
        {
            if (!File.Exists(dllFile)) return;
            Assembly assembly = null;
            try
            {
                assembly = Assembly.LoadFrom(dllFile);
            }
            catch (BadImageFormatException e)
            {
                log.Error("配置引擎无法加载DLL：" + dllFile, e);
            }
            if (assembly != null) InitAssembly(assembly);
            var extensions = new HashSet<ExtensionAttributeItem>();
            RegisterAssembly(assembly, extensions);
            foreach (ExtensionAttributeItem extension in extensions)
            {
                if (_extensionPoints.ContainsKey(extension.ExtensionAttribute.GetType()))
                {
                    _extensionPoints[extension.ExtensionAttribute.GetType()].Register(extension.Assembly,
                                                                                      extension.ExtensionAttribute);
                }
            }
            foreach (var extension in _extensionPoints.Values)
            {
                extension.OnLoad();
            }
        }

        /// <summary>
        /// 导入程序集
        /// </summary>
        /// <param name="dllPath">程序集文件路径</param>
        /// <returns></returns>
        public Assembly Import(string dllPath)
        {
            return Import(dllPath, true);
        }

        /// <summary>
        /// 导入程序集
        /// </summary>
        /// <param name="dllPath">The DLL path.</param>
        /// <param name="update">if set to <c>true</c> [update].</param>
        /// <returns></returns>
        public Assembly Import(string dllPath, bool update)
        {
            lock (_assemblys)
            {
                Assembly assembly = Assembly.LoadFile(dllPath);
                AssemblyName assemblyName = assembly.GetName();
                VersionAssembly verAssembly;
                if (_assemblys.ContainsKey(assemblyName.Name))
                {
                    verAssembly = _assemblys[assemblyName.Name];
                }
                else
                {
                    verAssembly = new VersionAssembly();
                    _assemblys[assemblyName.Name] = verAssembly;
                }
                Assembly old = verAssembly.CurrentVersionAssembly;
                verAssembly.Add(assembly, update);
                if (old != verAssembly.CurrentVersionAssembly)
                {
                    if (old != null)
                    {
                        UnRegisterAssembly(old);
                    }
                    RegisterAssembly(verAssembly.CurrentVersionAssembly, new HashSet<ExtensionAttributeItem>());
                }
                return verAssembly.LatestVersionAssembly;
            }
        }

        /// <summary>
        /// 使用版本
        /// </summary>
        /// <param name="assembly"></param>
        public void ApplyVersion(Assembly assembly)
        {
            lock (_assemblys)
            {
                AssemblyName assemblyName = assembly.GetName();
                if (_assemblys.ContainsKey(assemblyName.Name))
                {
                    VersionAssembly verAssembly = _assemblys[assemblyName.Name];
                    if (verAssembly.CurrentVersionAssembly != assembly)
                    {
                        UnRegisterAssembly(verAssembly.CurrentVersionAssembly);
                        verAssembly.SetVersion(assemblyName.Version);
                        RegisterAssembly(verAssembly.CurrentVersionAssembly, new HashSet<ExtensionAttributeItem>());
                    }
                }
            }
        }

        /// <summary>
        /// 获取所有版本的程序集
        /// </summary>
        /// <returns></returns>
        public VersionAssembly[] GetVersionAssemblys()
        {
            lock (_assemblys)
            {
                VersionAssembly[] result = _assemblys.Values.ToArray();
                Array.Sort(result
                           , new Comparison<VersionAssembly>(
                                 (assembly1, assembly2) => assembly1.Name.CompareTo(assembly2.Name)));
                return result;
            }
        }

        /// <summary>
        /// 根据程序集名称获取程序集
        /// </summary>
        /// <value></value>
        public VersionAssembly this[string name]
        {
            get
            {
                VersionAssembly versionAssembly;
                _assemblys.TryGetValue(name, out versionAssembly);
                return versionAssembly;
            }
        }

        private void InitAssembly(Assembly assembly)
        {
            AssemblyName assemblyName = assembly.GetName();
            VersionAssembly verAssembly;
            if (_assemblys.ContainsKey(assemblyName.Name))
            {
                verAssembly = _assemblys[assemblyName.Name];
            }
            else
            {
                verAssembly = new VersionAssembly();
                _assemblys[assemblyName.Name] = verAssembly;
            }
            if (verAssembly.IsLatestVersion(assembly))
            {
                verAssembly.Add(assembly);
            }
        }

        private readonly Dictionary<string, Type> _types = new Dictionary<string, Type>();

        private void RegisterAssembly(Assembly assembly, HashSet<ExtensionAttributeItem> extensions)
        {
            try
            {
                var extensionPointAttrs = assembly.GetCustomAttributes(typeof(ExtensionPointAttribute), true)
                                          as ExtensionPointAttribute[];
                if (extensionPointAttrs != null)
                    foreach (ExtensionPointAttribute extensionPointAttr in extensionPointAttrs)
                    {
                        _extensionPoints[extensionPointAttr.AttributeType] = extensionPointAttr.ExtensionPoint;
                        _types[extensionPointAttr.AttributeType.FullName] = extensionPointAttr.AttributeType;
                    }
                var extensionAttrs = assembly.GetCustomAttributes(typeof(ExtensionAttribute), true)
                                     as ExtensionAttribute[];
                if (extensionAttrs != null)
                    foreach (ExtensionAttribute extensionAttr in extensionAttrs)
                    {
                        if (_extensionPoints.ContainsKey(extensionAttr.GetType()))
                        {
                            _extensionPoints[extensionAttr.GetType()].Register(assembly, extensionAttr);
                        }
                        else if (extensions != null)
                        {
                            extensions.Add(new ExtensionAttributeItem(assembly, extensionAttr));
                        }
                    }
            }
            catch (Exception ex)
            {
                log.Error("OSGI 引擎调用RegisterAssembly(Assembly assembly, HashSet<ExtensionAttributeItem> extensions)方法异常", ex);
                if (!DisableExtAttrLoadException)
                    throw ex;
            }
        }

        private void UnRegisterAssembly(Assembly assembly)
        {
            var extensionAttrs = assembly.GetCustomAttributes(typeof(ExtensionAttribute), true)
                                 as ExtensionAttribute[];
            if (extensionAttrs != null)
                foreach (ExtensionAttribute extensionAttr in extensionAttrs)
                {
                    if (_extensionPoints.ContainsKey(extensionAttr.GetType()))
                    {
                        _extensionPoints[extensionAttr.GetType()].UnRegister(assembly, extensionAttr);
                    }
                }
            var extensionPointAttrs = assembly.GetCustomAttributes(typeof(ExtensionPointAttribute), true)
                                      as ExtensionPointAttribute[];
            if (extensionPointAttrs != null)
                foreach (ExtensionPointAttribute extensionPointAttr in extensionPointAttrs)
                {
                    _extensionPoints.Remove(extensionPointAttr.AttributeType);
                }
        }

        /// <summary>
        /// 扩展项
        /// <para>2011/3/4</para>
        /// 	<para>TANGJINGBO</para>
        /// 	<author>tangjingbo</author>
        /// </summary>
        internal sealed class ExtensionAttributeItem
        {
            /// <summary>
            /// 扩展项所属程序集
            /// </summary>
            /// <value>The assembly.</value>
            public Assembly Assembly { get; private set; }
            /// <summary>
            /// 获取扩展标记
            /// </summary>
            /// <value>The extension attribute.</value>
            public ExtensionAttribute ExtensionAttribute { get; private set; }

            /// <summary>
            /// 扩展项构造
            /// </summary>
            /// <param name="assembly">The assembly.</param>
            /// <param name="extensionAttribute">The extension attribute.</param>
            public ExtensionAttributeItem(Assembly assembly, ExtensionAttribute extensionAttribute)
            {
                Assembly = assembly;
                ExtensionAttribute = extensionAttribute;
            }
        }
    }
}